

namespace Stee.CAP8.MessageBus.Interface.Models
{
    using System;
    using System.Collections.Generic;
    using Stee.CAP8.MessageBus.Interface.Utility;

    /// <summary>
    /// Message for event publishing
    /// </summary>
    public class EventMessage
    {
        /// <summary>
        /// Gets or sets the mesage subscription topic
        /// </summary>
        public string Action { get; set; }

        /// <summary>
        /// Gets or sets the correlation ID
        /// </summary>
        public string CorrelationID { get; set; }

        /// <summary>
        /// Gets or sets the client ID
        /// </summary>
        public string ClientID { get; set; }

        /// <summary>
        /// Gets the command message source
        /// </summary>
        public string Source { get; set; }

        /// <summary>
        /// Gets or sets the JSON web token (JWT)
        /// </summary>
        public string JWT { get; set; }

        /// <summary>
        /// Gets or sets the command creation date time
        /// </summary>
        public DateTime CreateDT { get; set; }

        /// <summary>
        /// Gets or sets the message data/content
        /// </summary>
        public byte[] Data { get; set; }

        /// <summary>
        /// Gets the header collection mappings
        /// </summary>
        private readonly Dictionary<string, string> headers = new Dictionary<string, string>();


        /// <summary>
        /// Initializes a new instance of the CommandMessage class
        /// </summary>
        public EventMessage() { }


        /// <summary>
        /// Add new header referenced by key.
        /// If key already exist, current value will be over-written.
        /// </summary>
        /// <param name="key">Identifier for header value</param>
        /// <param name="value">Header string value</param>
        /// <returns>Return TRUE if added else FALSE</returns>
        public bool AddHeader(string key, string value)
        {
            if (ValidateHeaderKey(key))
            {
                this.headers[key] = value;
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Remove existing header by key
        /// </summary>
        /// <param name="key">Identifier for header value</param>
        /// <returns>Return TRUE if removed else FALSE</returns>
        public bool RemoveHeader(string key)
        {
            return this.headers.Remove(key);
        }

        /// <summary>
        /// Check for existing header key
        /// </summary>
        /// <param name="key">Identifier for header value</param>
        /// <returns>Return TRUE if </returns>
        public bool IsHeaderExist(string key)
        {
            if (ValidateHeaderKey(key))
            {
                return this.headers.ContainsKey(key);
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Get the header object by key identifier
        /// </summary>
        /// <param name="key">Key identifier</param>
        /// <returns>Return header object if found else NULL</returns>
        public object GetHeaderByKey(string key)
        {
            return this.IsHeaderExist(key) ? this.headers[key] : null;
        }

        /// <summary>
        /// Gets the header mappings
        /// </summary>
        /// <returns>Return header mappings</returns>
        public Dictionary<string, string> GetAllHeaders()
        {
            return new Dictionary<string, string>(this.headers);
        }

        /// <summary>
        /// Gets all class property values and header mappings
        /// </summary>
        /// <returns>Return property values & header mappings</returns>
        public Dictionary<string, string> GetAllHeadersAndProperties()
        {
            Dictionary<string, string> properties = new Dictionary<string, string>
            {
                { nameof(this.Action),          this.Action },
                { nameof(this.ClientID),        this.ClientID },
                { nameof(this.CorrelationID),   this.CorrelationID },
                { nameof(this.CreateDT),        JsonSerializer.SerializeString(this.CreateDT) },
                { nameof(this.JWT),             this.JWT },
                { nameof(this.Source),          this.Source },
            };

            foreach (var item in this.headers)
            {
                properties.Add(item.Key, item.Value);
            }

            return properties;
        }


        /// <summary>
        /// Gets a string that represents the current object
        /// </summary>
        /// <returns>Return current object string representation</returns>
        public override string ToString()
        {
            return $"{this.Action}_{this.ClientID}_{this.CorrelationID}";
        }


        /// <summary>
        /// Validate key name against class property names
        /// </summary>
        /// <param name="key">Header map key</param>
        /// <returns>Return TRUE if key does not match any class property names else FALSE</returns>
        private static bool ValidateHeaderKey(string key)
        {
            string k = key?.Trim();

            if (string.IsNullOrEmpty(k))
            {
                throw new ArgumentNullException(nameof(key));
            }
            else
            {
                return !(string.Equals(k, nameof(Action), StringComparison.InvariantCultureIgnoreCase) ||
                    string.Equals(k, nameof(ClientID), StringComparison.InvariantCultureIgnoreCase) ||
                    string.Equals(k, nameof(CorrelationID), StringComparison.InvariantCultureIgnoreCase) ||
                    string.Equals(k, nameof(CreateDT), StringComparison.InvariantCultureIgnoreCase) ||
                    string.Equals(k, nameof(Data), StringComparison.InvariantCultureIgnoreCase) ||
                    string.Equals(k, nameof(JWT), StringComparison.InvariantCultureIgnoreCase) ||
                    string.Equals(k, nameof(Source), StringComparison.InvariantCultureIgnoreCase) ||
                    string.Equals(k, nameof(headers), StringComparison.InvariantCultureIgnoreCase));
            }
        }


        /// <summary>
        /// Generate new event message
        /// </summary>
        /// <param name="data">Event message data value</param>
        /// <param name="properties">Meta-data containing both message property values & headers</param>
        /// <returns>Return new Event message instance</returns>
        public static EventMessage Generate(byte[] data, IDictionary<string, byte[]> properties)
        {
            //// Create command message
            EventMessage message = new EventMessage
            {
                Data = data,
            };

            message.Action = JsonSerializer.Deserialize<string>(properties?[nameof(Action)]);
            message.ClientID = JsonSerializer.Deserialize<string>(properties?[nameof(ClientID)]);
            message.CorrelationID = JsonSerializer.Deserialize<string>(properties?[nameof(CorrelationID)]);
            message.JWT = JsonSerializer.Deserialize<string>(properties?[nameof(JWT)]);
            message.Source = JsonSerializer.Deserialize<string>(properties?[nameof(Source)]);

            var dtStr = JsonSerializer.Deserialize<string>(properties?[nameof(CreateDT)]);
            message.CreateDT = JsonSerializer.DeserializeString<DateTime>(dtStr);

            //// Build message header mappings
            foreach (var item in properties)
            {
                message.AddHeader(item.Key, JsonSerializer.Deserialize<string>(item.Value));
            }

            return message;
        }
    }
}
